import 'dart:ui';

import 'package:flutter/material.dart';

/// 弹出路由
class SlideUpRoute<T> extends PageRoute<T> {
  SlideUpRoute({
    required this.builder,
    this.maintainState = true,
    this.barrierColor,
    this.curve = Curves.ease,
    RouteSettings? settings,
  }) : super(settings: settings, fullscreenDialog: false);

  final WidgetBuilder builder;

  final Curve curve;

  @override
  final bool maintainState;

  @override
  final Color? barrierColor;

  @override
  bool get opaque => false;

  @override
  String? get barrierLabel => null;

  @override
  Duration get transitionDuration => const Duration(milliseconds: 300);

  late AnimationController _animationController;

  @override
  AnimationController createAnimationController() {
    _animationController = AnimationController(duration: transitionDuration, vsync: navigator!);
    return _animationController;
  }

  @override
  Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
    return _SlideTransition(
      animationController: _animationController,
      start: Alignment.bottomCenter,
      curve: curve,
      navigator: navigator!,
      child: Semantics(
        scopesRoute: true,
        explicitChildNodes: true,
        child: builder(context),
      ),
    );
  }
}

/// slide构建器
class _SlideTransition extends StatefulWidget {
  const _SlideTransition({
    Key? key,
    required this.animationController,
    required this.child,
    required this.navigator,
    required this.start,
    required this.curve,
  }) : super(key: key);

  final AnimationController animationController;

  final Curve curve;

  final Widget child;

  final NavigatorState navigator;

  final Alignment start;

  @override
  _SlideTransitionState createState() => _SlideTransitionState();
}

class _SlideTransitionState extends State<_SlideTransition> {
  @override
  void initState() {
    super.initState();
  }

  final double _childH = MediaQueryData.fromWindow(window).size.height;

  double get _childHeight => _childH;

  bool get _dismissUnderway => widget.animationController.status == AnimationStatus.reverse;

  void _pop() {
    if (widget.navigator.canPop()) widget.navigator.pop();
  }

  void _handleDragStart(DragStartDetails details) {
    if (widget.animationController.isCompleted) {
      widget.navigator.didStartUserGesture();
    }
  }

  void _handleDragUpdate(DragUpdateDetails details) {
    final double min = widget.animationController.value - details.primaryDelta! / _childHeight;
    if (_dismissUnderway || min <= 0) return;

    widget.animationController.value -= details.primaryDelta! / _childHeight;
  }

  void _handleDragEnd(DragEndDetails details) {
    if (_dismissUnderway) return;

    bool isClosing = false;
    if (details.velocity.pixelsPerSecond.dy > _childHeight) {
      final double flingVelocity = -details.velocity.pixelsPerSecond.dy / _childHeight;
      if (widget.animationController.value > 0.0) {
        widget.animationController.fling(velocity: flingVelocity);
      }
      if (flingVelocity < 0.0) {
        isClosing = true;
      }
    } else if (widget.animationController.value < 0.5) {
      if (widget.animationController.value > 0.0) widget.animationController.fling(velocity: -1.0);
      isClosing = true;
    } else {
      widget.animationController.forward();
    }

    if (isClosing) {
      _pop();
    }

    if (widget.animationController.isAnimating) {
      late AnimationStatusListener animationStatusCallback;
      animationStatusCallback = (AnimationStatus status) {
        if (widget.navigator.userGestureInProgress) widget.navigator.didStopUserGesture();

        widget.animationController.removeStatusListener(animationStatusCallback);
        if (_dismissUnderway) {
          _pop();
        }
      };
      widget.animationController.addStatusListener(animationStatusCallback);
    } else {
      widget.navigator.didStopUserGesture();
    }
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onVerticalDragStart: _handleDragStart,
      onVerticalDragUpdate: _handleDragUpdate,
      onVerticalDragEnd: _handleDragEnd,
      child: SlideTransition(
        position: Tween<Offset>(begin: _handleStart, end: Offset.zero).animate(CurvedAnimation(
          parent: widget.animationController,
          curve: widget.curve,
        )),
        child: widget.child,
      ),
    );
  }

  ///处理开始点
  Offset get _handleStart {
    Offset start = const Offset(0, 1);
    if (widget.start == Alignment.bottomLeft) {
      start = const Offset(-1, 1);
    } else if (widget.start == Alignment.bottomRight) {
      start = const Offset(1, 1);
    } else if (widget.start == Alignment.topCenter) {
      start = const Offset(0, -1);
    } else if (widget.start == Alignment.topLeft) {
      start = const Offset(-1, -1);
    } else if (widget.start == Alignment.topRight) {
      start = const Offset(1, -1);
    }

    return start;
  }
}

///slide变换配置
class SlideTransitionConfig {
  SlideTransitionConfig(this.start);

  Alignment start;
}
